package praktikumid.p11_k;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Kimp extends JFrame {

	abstract class Shape {
		public Color color = Color.BLUE;
		
		public boolean selected;
		
		public abstract Shape copy();
		
		protected void copyHelper(Shape s) {
			s.selected = selected;
			s.color = new Color(color.getRGB());
		}
		
		public abstract void draw(Point p);
		public abstract void move(int dx, int dy);
		
		public void paint(Graphics g) {
			if (selected) {
				g.setColor(Color.RED);
			} else {
				g.setColor(color);
			}
		}
		
		public abstract int getDistance(Point p);
	}
	
	class Rectangle extends Shape {
		public Point start;
		public Point end;
		
		public Rectangle() {
			
		}

		public Rectangle(Point p) {
			start = p;
		}
		
		@Override
		public void paint(Graphics g) {
			super.paint(g);
			Point upperLeft = new Point(
					Math.min(start.x, end.x),
					Math.min(start.y, end.y));
			int width = Math.abs(start.x - end.x);
			int height = Math.abs(start.y - end.y);
			
			g.drawRect(upperLeft.x, upperLeft.y, width, height);
		}

		@Override
		public Rectangle copy() {
			Rectangle r = new Rectangle();
			copyHelper(r);
			r.start = new Point(start.x, start.y);
			r.end = new Point(end.x, end.y);
			return r;
		}

		@Override
		public void draw(Point p) {
			end = p;			
		}

		@Override
		public int getDistance(Point p) {
			int d1 = (int) p.distance(start);
			int d2 = (int) p.distance(end);
			return Math.min(d1, d2);
		}

		@Override
		public void move(int dx, int dy) {
			start.x += dx;
			start.y += dy;
			end.x += dx;
			end.y += dy;
		}
		
		
		
	}
	
	class Circle extends Shape {

		Point start;
		Point end;
		
		public Circle(Point p) {
			this.start = p;
			this.end = p;
		}
		
		public Circle() {
		}
		@Override
		public Shape copy() {
			// TODO Auto-generated method stub
			Circle c = new Circle();
			copyHelper(c);
			c.start = new Point(start.x, start.y);
			c.end = new Point(end.x, end.y);
			return c;
		}

		@Override
		public void draw(Point p) {
			this.end = p;
		}

		@Override
		public void move(int dx, int dy) {
			start.x += dx;
			start.y += dy;
			end.x += dx;
			end.y += dy;
		}

		@Override
		public int getDistance(Point p) {
			// TODO Auto-generated method stub
			Point center = new Point(
					(start.x + end.x) / 2,
					(start.y + end.y) / 2
					);
			return (int)p.distance(center);
		}
		
		public void paint(Graphics g) {
			super.paint(g);
			Point upperLeft = new Point(
					Math.min(start.x, end.x),
					Math.min(start.y, end.y));
			int width = Math.abs(start.x - end.x);
			int height = Math.abs(start.y - end.y);
			g.drawOval(upperLeft.x, upperLeft.y, width, height);
		}
	}
	
	class ShapeList {
		ArrayList<ArrayList<Shape>> shapes = new ArrayList<ArrayList<Shape>>();
		
		int stateIndex = 0;
		
		public ShapeList() {
			shapes.add(new ArrayList<Shape>());
		}
		
		public ArrayList<Shape> getShapes() {
			return shapes.get(stateIndex);
		}
		public void addShape(Shape s) {
			newState();
			shapes.get(stateIndex).add(s);
		}
		
		public void newState() {
			int shapesSize = shapes.size();
			for (int i = shapesSize - 1; i > stateIndex; i--) {
				shapes.remove(i);
			}
			// copy elements
			shapes.add(new ArrayList<Shape>());
			ArrayList<Shape> newState = shapes.get(stateIndex + 1);
			for (Shape s : getShapes()) {
				newState.add(s.copy());
			}
			this.stateIndex++;
		}
		
		public Shape getLastShape() {
			int size = shapes.get(stateIndex).size();
			if (size > 0) {
				return shapes.get(stateIndex).get(size - 1);
			}
			return null;
		}
		
		public Shape getSelectedShape() {
			System.out.println("state:" + stateIndex);
			for (Shape s : getShapes()) {
				if (s.selected) return s;
			}
			return null;
		}
		
		public boolean undo() {
			if (stateIndex > 0) {
				stateIndex--;
				return true;
			}
			return false;
		}
		
		public boolean redo() {
			if (stateIndex < shapes.size() - 1) {
				stateIndex++;
				return true;
			}
			return false;
		}
		
		public void select(Point p) {
			unselectAll();
			int minDist = 1000;
			Shape closeShape = null;
			for (Shape s : getShapes()) {
				int dist = s.getDistance(p);
				if (dist < minDist) {
					minDist = dist;
					closeShape = s;
				}
			}
			if (closeShape != null) {
				closeShape.selected = true;
			}
		}
		
		public void unselectAll() {
			for (Shape s : getShapes()) {
				s.selected = false;
			}
		}
	}
	
	public ShapeList shapeList = new ShapeList();
	
	public boolean selectMode = false;
	public Point lastPoint = null;
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Kimp k = new Kimp();
	}
	
	public Kimp() {
		this.setTitle("Blah");
		this.setSize(800, 600);
		this.setVisible(true);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		this.addMouseListener(new MouseAdapter() {
			@Override
			public void mousePressed(MouseEvent e) {
				// TODO Auto-generated method stub
				super.mousePressed(e);
				
				if (selectMode) {
					shapeList.newState();
					lastPoint = e.getPoint();
				} else {
					if (SwingUtilities.isLeftMouseButton(e)) {
						// draw new stuff
						Rectangle r = new Rectangle(e.getPoint());
						r.end = e.getPoint();
						shapeList.addShape(r);
					} else {
						Circle c = new Circle(e.getPoint());
						shapeList.addShape(c);
					}
					repaint();
				}
			}
		});
		
		this.addMouseMotionListener(new MouseMotionAdapter() {
			@Override
			public void mouseDragged(MouseEvent e) {
				super.mouseDragged(e);
				
				if (selectMode) {
					Shape s = shapeList.getSelectedShape();
					if (s != null) {
						System.out.println("jee");
						s.move(e.getPoint().x - lastPoint.x, e.getPoint().y - lastPoint.y);
						repaint();
					}
					lastPoint = e.getPoint();
				} else {
					Shape s = shapeList.getLastShape();
					if (s != null) {
						s.draw(e.getPoint());
						repaint();
					}
				}
			}
			
			@Override
			public void mouseMoved(MouseEvent e) {
				// TODO Auto-generated method stub
				super.mouseMoved(e);
				
				if (selectMode) {
					shapeList.select(e.getPoint());
					repaint();
				}
			}
		});
		
		this.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				// TODO Auto-generated method stub
				super.keyPressed(e);
				
				if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
					boolean undone = shapeList.undo();
					if (!undone) {
						System.out.println("EXIT?");
					}
					repaint();
				}
				
				if (e.getKeyCode() == KeyEvent.VK_SPACE) {
					shapeList.redo();
					repaint();
				}
				
				if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
					selectMode = true;
				}
			}
			
			@Override
			public void keyReleased(KeyEvent e) {
				// TODO Auto-generated method stub
				super.keyReleased(e);
				if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
					selectMode = false;
					shapeList.unselectAll();
				}
			}
		});
	}
	
	@Override
	public void paint(Graphics g) {
		// TODO Auto-generated method stub
		super.paint(g);
		
		for (Shape s : shapeList.getShapes()) {
			s.paint(g);
		}
	}

}
